home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power Programmierung
/
Power-Programmierung (Tewi)(1994).iso
/
assemblr
/
library
/
edit
/
fremacsr
/
buffers.asm
next >
Wrap
Assembly Source File
|
1986-11-16
|
25KB
|
1,185 lines
;History:1045,7,17
test_device equ 0
.xlist
include memory.def
bufseg segment public
next_buffer dw ?
extrn toptop: word
extrn topbot: word
extrn bottop: word
extrn botbot: word
extrn memsize: word
extrn bufseg_size: word
bufseg ends
data segment byte public
extrn phd_seg: word
public textseg
textseg dw ?
first_buffer dw ? ;segment of first buffer
last_para dw ? ;segment after highest buffer
limit_para dw ? ;segment after highest available.
fcb_struc struc
fcb_drive db 0
fcb_fname db ' '
fcb_ext db ' '
fcb_curblk dw 0
fcb_recsz dw 0
fcb_filsz dw 0,0
fcb_date dw 0
fcb_time dw 0
fcb_res dd ?,?
fcb_currec db 0
fcb_ranrec dw 0,0
fcb_struc ends
device_header struc
device_next dd ?
device_attr dw ?
device_strategy dw ?
device_intr dw ?
device_name db ' '
device_header ends
;static request header.
srh_struc struc
srh_len db ?
srh_unit db ?
srh_cmd db ?
srh_status dw ?
srh_res dd ?,?
srh_struc ends
;static request header for reading and writing.
srh_rw struc
db (size srh_struc) dup(?)
crw_media db ?
crw_taddr dd ?
crw_cnt dw ?
crw_start dw ?
srh_rw ends
;static request header for nondestructive read no wait.
srh_ic struc
db (size srh_struc) dup(?)
cic_char db ?
srh_ic ends
segofs struc
offs dw ?
segm dw ?
segofs ends
nul_fcb fcb_struc <0,'NUL ',' '>
our_device_name db '@',0
their_end dd ?
eof_mark db ']'
extrn inverse_flag: byte
db 256 dup(?)
mystack label byte
md_ctab label word
dw md_unk ; initialize
dw md_unk ; media check
dw md_unk ; build bpb
dw md_unk ; input control string
dw md_in ; input
dw md_ichk ; input check (buffer status)
dw md_istat ; input status
dw md_succ ; input flush
dw md_out ; output
dw md_out ; output with verify
dw md_ostat ; output status
dw md_succ ; output buffer flush
dw md_unk ; output control string
crlf_string db 0dh,0ah
data ends
code segment byte public
;all the routines in this segment are entered with ds=data, es=data
assume cs:code, ds:data, es:data, ss:data
;the following externs are in 'memory'
extrn read_mark: near
extrn del_to_mark: near
;the following externs are in 'redisplay'
extrn redisplay: near
extrn paint_screen: near
extrn init_entry: near
extrn uninit_exit: near
extrn abort_fatal: near ;fatal error handler
our_sp dw ?
our_ss dw ?
ptrsav dd ?
their_sp dw ?
their_ss dw ?
our_device device_header<-1,8000h, md_strat, md_intr, '@ '>
lf_flag db 0 ;=1 if we should return LF next input.
parameters dw 0
dw 80h, ?
dw 5ch, ?
dw 6ch, ?
copy_to_dioa:
;enter with si, cx->command.
;exit with cs:phd_dioa set to the string.
push es
mov es,phd_seg ;copy the command to phd_dioa+1
mov di,80h+1
if 0
mov ax,3700h ;get the switch char.
int 21h
mov al,dl
stosb
mov al,'C' ;store -C or /C.
stosb
endif
rep movsb
mov al,CR ;store the terminating CR.
stosb
sub di,80h+1+1 ;don't count the CR.
mov ax,di
mov es:[80h],al ;store the count.
pop es ;restore es.
ret
public execute_program
execute_program:
;enter with si, cx->path of filter to execute, di ->null terminated filename.
;exit with ax=return result.
push di
call copy_to_dioa
call init_device
pop di
call actually_execute
push ax
call uninit_device
pop ax
ret
public execute_filter
execute_filter:
;enter with:
; si, cx->path of filter to execute,
; al=mark to filter to.
; di ->null terminated filename.
;exit with ax=return result.
mov eof_mark,al
push di
call copy_to_dioa
call init_device
pop di
mov bx,0 ;get a copy of stdin.
mov ah,45h
int 21h
push ax ;remember the old handle.
mov bx,1 ;get a copy of stdout.
mov ah,45h
int 21h
push ax ;remember the old handle.
mov dx,offset our_device_name
mov ax,3d02h ;open for read/write.
int 21h
mov bx,ax ;remember the handle.
mov cx,0 ;force stdin to the per device.
mov ah,46h
int 21h
mov cx,1 ;force stdout to the per device.
mov ah,46h
int 21h
mov ah,3eh ;close the original handle.
int 21h
mov bx,0 ;set stdin to raw.
mov ax,4400h ;get the device info.
int 21h
mov dh,0
or dl,20h ;set to raw mode.
mov ax,4401h ;set the device info.
int 21h
call actually_execute
mov dx,ax
mov bx,1 ;close stdout.
mov ah,3eh
int 21h
pop bx ;unredirect stdout.
mov ah,46h
mov cx,1
int 21h
mov bx,0 ;close stdin.
mov ah,3eh
int 21h
pop bx ;unredirect stdin.
mov cx,0
mov ah,46h
int 21h
push dx ;save the result code.
call uninit_device
pop ax
ret
actually_execute:
;enter with di -> filename, cs:phd_dioa = arguments.
push di
call uninit_exit
pop di
push ds
push es
push di
call compact_buffers ;make room for the program.
pop di
mov ds,dx ;get the para of the last buffer.
assume ds:bufseg
mov bx,dx ;remember the last buffer here.
call buffer_paragraphs
add bx,cx ;compute the first free segment.
mov dx,phd_seg ;subtract off the allocated segment.
sub bx,dx
mov es,dx ;get es=allocated segment.
assume es:nothing
mov ah,4ah ;reduce ourself in size.
int 21h
mov our_sp,sp ;remember our stack.
mov our_ss,ss
mov ds,phd_seg ;move ds first because we need ds.
assume ds:nothing
mov es,phd_seg
assume es:nothing
mov dx,di ;set up to execute the program.
mov ax,2901h ;parse fcb1.
mov si,81h ;->phd_sdioa.
mov di,5ch ;->phd_fcb1
int 21h
mov ax,2901h ;parse fcb2.
mov di,6ch ;->phd_fcb2.
int 21h
push ss ;ss:dx -> filename to execute.
pop ds
assume ds:data
push cs
pop es
assume es:code
mov bx,offset parameters
mov ax,phd_seg
mov es:[bx]+4,ax ;use original phd parameters.
mov es:[bx]+8,ax
mov es:[bx]+12,ax
mov ax,4b00h
int 21h
jc actually_execute_1
xor ax,ax ;make sure ax is zero if no errors.
actually_execute_1:
cli ;get our stack back.
mov ss,cs:our_ss
mov sp,cs:our_sp
sti
push ax
mov bx,0ffffh ;now grab all of memory again.
mov es,phd_seg
mov ah,4ah ;see how much is available.
int 21h
mov ah,4ah ;grab all of it.
int 21h
push cs ;reset the fatal error address.
pop ds
mov dx,offset abort_fatal
mov ax,2524h
int 21h
mov ax,33h*256+1 ;turn break checking back off.
mov dl,0 ; in case someone turned it on.
int 21h
pop ax
pop es
pop ds
assume ds:data, es:data
push ax
call init_entry
call paint_screen
pop ax
ret
init_device:
push es
mov dx,offset nul_fcb
mov ah,0fh ;opfile
int 21h
les di,dword ptr nul_fcb.fcb_res+1 ;get the device driver pointer for version 2.
mov ah,30h ;see if it's really version 2.
int 21h
cmp al,2
je init_device_1 ;it is, we have it.
les di,dword ptr nul_fcb.fcb_res+2 ;get the device driver pointer for version 3.
init_device_1:
mov their_end.offs,di ;remember where we were
mov their_end.segm,es
les di,dword ptr es:[di].device_next
cmp di,-1 ;is this the end of the list?
jne init_device_1 ;no - keep looking.
les di,their_end ;get the pointer to the end.
mov es:[di].device_next.offs,offset our_device
mov es:[di].device_next.segm,cs
pop es
ret
uninit_device:
push es
les di,their_end ;make es:di -> end of the device chain.
mov ax,-1 ;make this the end again.
stosw
stosw
pop es
ret
; this is the dispatch table
; there are 2 entry points to a drive, the strategy entry point
; and the interrupt entry point. when z-dos wishes to do an i/o
; request, it first calls the strategy routine with a dword pointer
; in the es:bx registers. the strategy routine simply saves this
; pointer and returns to the system. the interrupt routine is then
; called by z-dos. the interrupt routine retrieves the dword pointer
; saved by the strategy routine. this pointer is the address of a
; "request packet" that contains the information needed to carry
; out the i/o request, such as transfer address, byte count, etc.
; the strategy routine
md_strat proc far
mov word ptr cs:ptrsav,bx
mov word ptr cs:ptrsav+2,es ; save es:bx as dword
ret
md_strat endp
; now the interrupt routine.
md_intr proc far
; save everything
mov cs:their_ss,ss
mov cs:their_sp,sp
cli
mov ss,cs:our_ss
mov sp,offset mystack
sti
push ax
push bx
push cx
push dx
push si
push di
push bp
push es
push ds
if test_device
mov si,word ptr cs:ptrsav ; load ax:si with buffer addr
mov ax,word ptr cs:ptrsav+2 ; load ax:si with buffer addr
mov cx,(size srh_rw) ;get count of bytes to transfer
mov es,cs:our_ss
mov ds,es:textseg
call insert_string$
endif
les bx,cs:ptrsav ; es:bx = request packet
mov cl,es:[bx].srh_cmd ; cl = requested command
xor ch,ch
shl cx,1 ; make into word offset
mov si,cx ; si = pointer to routine
jmp ss:md_ctab[si] ; go to it
;* md_in - character input
;
; md_in reads the number of characters requested into the
; address given in the request packet.
;
md_in:
mov cx,es:[bx].crw_cnt ;get the transfer count.
les di,es:[bx].crw_taddr
jcxz md_in_2
md_in_1:
push es
push di
push cx
cmp cs:lf_flag,0 ;do we have a LF to return?
je get_char_1 ;no.
mov cs:lf_flag,0 ;clear the flag, return LF.
mov al,LF
jmp short get_char_3
get_char_1:
mov es,cs:our_ss ;prepare to call read_mark.
mov ds,cs:our_ss
push ds
mov al,eof_mark ;read the next char.
call read_mark
jc get_char_5 ;go if the mark is after the point.
xor cx,cx ;mark is before point - eof now.
get_char_5:
lodsw ;this might not be a valid char.
pop ds
jcxz get_char_4 ;if no chars left, don't delete any!
push ax ;now delete this char.
push cx
mov al,'>'
call del_to_mark
pop cx
pop ax
get_char_4:
stc
jcxz get_char_2 ;no more chars - return cy.
cmp cx,2 ;do we have less than two chars?
jb get_char_3 ;yes - must be a single char.
cmp ax,CR + LF*256 ;do we have a newline?
jne get_char_3 ;no - must be a single char.
mov al,CR ;newline - return CR.
inc cs:lf_flag ;next time return a LF.
get_char_3:
clc
get_char_2:
pop cx
pop di
pop es
jc md_in_2 ;eof before count satisfied.
stosb
loop md_in_1
md_in_2:
les bx,cs:ptrsav ; es:bx = packet
sub es:[bx].crw_cnt,cx ;subtract off the bytes not read.
jmp md_succ
;* md_ichk - non-destructive input check
;
; md_ichk returns the status of the input queue. a
; return of busy indicates queue empty, otherwise
; return character at front of queue (without removing
; it).
;
md_ichk:
mov es,cs:our_ss ;prepare to call read_mark.
mov ds,cs:our_ss
mov al,eof_mark ;read the next char.
call read_mark
lodsb ;this might not be a valid char.
les bx,cs:ptrsav ; es:bx = packet
mov es:[bx].cic_char,al
jcxz md_ichk_1 ;no more chars - return busy.
jmp md_succ
md_ichk_1:
mov ax,300h ;return busy.
jmp md_exit
;* md_istat - input status
;
; md_istat sets the busy bit if there is a character
; ready to be read. if busy is set, the i/o will have
; to go to the physical device. if busy is clear, the
; read will return quickly.
;
md_istat:
jmp md_succ ; show always ready
;* md_ifl - input queue flush
;
; flush the input queue
;
md_ifl:
jmp md_succ ; consider it done!
;* md_out - device output
;
; md_out sends the output to the device
;
md_out:
mov si,es:[bx].crw_taddr.offs ; load ax:si with buffer addr
mov ax,es:[bx].crw_taddr.segm ; load ax:si with buffer addr
mov cx,es:[bx].crw_cnt ;get count of bytes to transfer
mov es,cs:our_ss
mov ds,es:textseg
call insert_string$
jmp md_succ ;show success.
;* md_ostat - output status
;
; md_ostat returns the status of the device. if busy
; is a 1, output will have to wait for device ready. if
; busy is a 0, output will go directly to device with
; no waiting.
;
md_ostat:
jmp md_succ ; output device is ready
; the routines exit through md_succ if no error, or md_fail if
; an error has occured
md_succ:
mov ax,100h ; show 'done' with no errors.
jmp short md_exit
md_unk:
mov al,3 ; unknown command
md_fail:
mov ah,80h ; add the error bits
md_exit:
les bx,cs:ptrsav ; es:bx = packet
mov es:srh_status[bx],ax ; save the status
if test_device
mov si,word ptr cs:ptrsav ; load ax:si with buffer addr
mov ax,word ptr cs:ptrsav+2 ; load ax:si with buffer addr
mov cx,(size srh_rw) ;get count of bytes to transfer
mov es,cs:our_ss
mov ds,es:textseg
call insert_string$
les bx,cs:ptrsav
mov si,es:[bx].crw_taddr.offs ; load ax:si with buffer addr
mov ax,es:[bx].crw_taddr.segm ; load ax:si with buffer addr
mov cx,es:[bx].crw_cnt ;get count of bytes to transfer
jcxz md_exit_1
mov es,cs:our_ss
mov ds,es:textseg
call insert_string$
md_exit_1:
mov si,offset crlf_string ; load ax:si with buffer addr
mov ax,ss ; load ax:si with buffer addr
mov cx,2 ;get count of bytes to transfer
mov es,cs:our_ss
mov ds,es:textseg
call insert_string$
endif
pop ds
pop es
pop bp
pop di
pop si
pop dx
pop cx
pop bx
pop ax
cli
mov ss,cs:their_ss
mov sp,cs:their_sp
sti
ret
md_intr endp
public get_next_buffer
get_next_buffer:
push ds
mov ds,textseg
assume ds:bufseg
mov ax,next_buffer
pop ds
assume ds:data
ret
public init_all_buffers
init_all_buffers:
;enter with ax=>first paragraph of available memory, bx=> first paragraph of
; unavailable memory.
;exit with cy if no buffer available.
mov first_buffer,ax
mov last_para,bx
mov textseg,ax
mov dx,ax
call init_buffer
ret
public buffer_allocate
buffer_allocate:
;entry:
; case cx of
; -1..-32768: report the current buffer number.
; exit: ax=current buffer number.
; 0: create a new buffer.
; exit: ax=new buffer number if enough memory, ax=0 otherwise.
; 1..32767:
; entry: cx=buffer number to select, ax=0 for read/write buffer.
; exit: ax=buffer number if it exists, ax=0 otherwise.
jcxz buffer_allocate_2
or cx,cx ;if cx<0, return buffer number.
js buffer_allocate_4_j
push cx
push ax
call find_buffer
jc buffer_allocate_5 ;buffer not found.
push ds
push dx
mov ds,dx ;get the current buffer back.
call select_buffer
pop dx
pop ds
pop cx ;pushed as ax =0 if read/write buffer.
pop ax ;pushed as cx (buffer number).
or cx,cx ;read only buffer?
jne buffer_allocate_4 ;yes - we're done.
push ax
call close_up_buffers
pop cx ;pushed as ax
mov dx,first_buffer ;find the buffer again.
assume ds:bufseg
buffer_allocate_6:
mov ds,dx
mov dx,next_buffer
loop buffer_allocate_6
mov dx,ds ;get the current buffer back.
push es
pop ds
assume ds:data
call open_up_buffers
buffer_allocate_4_j:
jmp short buffer_allocate_4
buffer_allocate_5:
add sp,4 ;conserve the stack.
mov ax,0 ;buffer not found.
jmp short buffer_allocate_1
buffer_allocate_2:
call close_up_buffers ;close all the buffers up.
mov ds,dx ;get the para of the last buffer.
assume ds:bufseg
call buffer_paragraphs ;compute the size of it.
add dx,cx ;find the end of it.
call init_buffer
mov ax,0
jc buffer_allocate_1 ;not enough memory.
mov next_buffer,dx ;point previous buffer to this one.
buffer_allocate_4:
mov bx,textseg
call buffer_number ;return number in ax.
buffer_allocate_1:
push es
pop ds
assume ds:data
ret
public buffer_insert
buffer_insert:
;enter with al=mark, cx=buffer number.
;insert the text between point and mark from the given buffer.
;exit with nc if ok, cy if the given buffer doesn't exist, or the specified
; text won't fit.
push textseg
push ax
call find_buffer ;find their buffer.
assume ds:bufseg
jc buffer_insert_1 ;not found.
push ds
push dx
mov ds,dx ;get the current buffer back.
call select_buffer
pop dx
pop ds
pop ax
push dx
call read_mark
pop dx
pop ds ;make ds->our buffer.
push dx ;save ->their buffer.
push cx
call select_buffer
pop cx
pop ax ;pushed as dx.
call insert_string$
jmp short buffer_insert_2
buffer_insert_1:
add sp,4 ;get rid of mark, textseg.
stc
buffer_insert_2:
push es ;restore ds.
pop ds
assume ds:data
ret
public buffer_number
buffer_number:
;enter with bx=paragraph of buffer.
;exit with ax=number of buffer.
push ds
assume ds:bufseg
mov dx,first_buffer
xor ax,ax
buffer_number_1:
inc ax
mov ds,dx
cmp dx,bx ;is this the one we're looking for.
mov dx,next_buffer ;in any case, get the next buffer
jne buffer_number_1
buffer_number_2:
pop ds
assume ds:data
ret
public succ_buffer
succ_buffer:
;enter with bx=buffer number.
;exit with nc, bx=next nonempty buffer, dx=segment of next buffer,
; cy if no other buffers or all other buffers are empty.
push ds
mov cx,bx
mov ax,bx
mov dx,first_buffer
assume ds:bufseg
succ_buffer_2:
cmp dx,last_para ;at the end?
stc
je succ_buffer_3 ;yes - don't change the buffer number.
mov ds,dx
mov dx,next_buffer
loop succ_buffer_2
succ_buffer_0:
cmp dx,last_para ;are we at the end?
jne succ_buffer_1
mov ds,first_buffer ;yes - start from the beginning again.
mov dx,next_buffer ;skip the first buffer.
mov ax,1
succ_buffer_1:
inc ax ;preincrement.
cmp ax,bx ;have we wrapped around?
stc
je succ_buffer_3 ;yes - exit.
mov ds,dx ;get the new segment to try.
mov dx,topbot ;is the next buffer empty?
sub dx,toptop
add dx,botbot
sub dx,bottop
mov dx,next_buffer
je succ_buffer_0 ;yes - keep looking.
mov bx,ax ;return the new buffer number.
clc
succ_buffer_3:
mov dx,ds ;remember the segment.
assume ds:data
pop ds
ret
;the private routines start here.
open_up_buffers:
;enter with dx=paragraph of buffer to open up.
mov bx,last_para ;search for the last buffer in memory.
mov cx,bx ;new next buffer is the same as the old.
assume ds:bufseg
; search for the buffer whose next_buffer is in bx. We start with
; bx=last_para, then we work our way down through the buffers.
; We use cx to hold the new next buffer for the buffer we're looking for.
open_up_buffers_1:
mov ax,first_buffer
open_up_buffers_2:
mov ds,ax ;switch to this buffer.
mov ax,next_buffer ;is this the last one?
cmp ax,bx
jne open_up_buffers_2 ;no - keep looking.
; now that we have the buffer we're looking for, tell it where the next
; buffer is in memory.
mov next_buffer,cx
mov bx,ds ;get the current buffer.
cmp bx,dx ;is it the one we want?
je open_up_buffers_3 ;yes - go open it.
;ds, bx now has the buffer to move up in memory.
mov ax,cx ;get the destination paragraph.
call move_buffer_higher ;move the buffer up.
mov cx,ax ;save the new destination paragraph.
mov bx,ds ;save the "next" buffer.
jmp open_up_buffers_1
open_up_buffers_3:
call open_buffer ;open the buffer in ds.
push es
pop ds
assume ds:data
ret
compact_buffers:
;move all the buffers as low in memory as they'll go.
;exit with dx=paragraph of last buffer in memory.
push ds
mov ds,first_buffer
assume ds:bufseg
compact_buffers_4:
mov bx,next_buffer
cmp bx,last_para ;was this the last buffer in memory?
je compact_buffers_3 ;yes - we're done.
call move_buffer_lower ; ax=paragraph to move it to.
jmp compact_buffers_4
compact_buffers_3:
mov dx,ds ;return the para of the last buffer.
pop ds
assume ds:data
ret
close_up_buffers:
;close the buffer that is open.
;exit with dx=paragraph of last buffer in memory.
push ds
mov ds,first_buffer
assume ds:bufseg
close_up_buffers_1:
mov bx,next_buffer
cmp bx,last_para ;was this the last buffer in memory?
je close_up_buffers_2 ;yes - we're done.
call close_buffer ;close this buffer.
call move_buffer_lower ; ax=paragraph to move it to.
jmp close_up_buffers_1
close_up_buffers_2:
mov dx,ds ;return the para of the last buffer.
pop ds
assume ds:data
ret
public find_buffer
find_buffer:
;enter with cx=buffer number.
;exit with nc, dx set to that buffer if it exists, cy otherwise.
mov dx,first_buffer
assume ds:bufseg
find_buffer_1:
cmp dx,last_para ;at the end?
je find_buffer_2
mov ds,dx
mov dx,next_buffer
loop find_buffer_1
mov dx,ds ;get the current buffer back.
push es
pop ds
clc
ret
find_buffer_2:
push es ;restore the data segment.
pop ds
assume ds:data
stc
ret
code ends
code segment byte public
;all the code in this segment is entered with ds=bufseg, es=data
assume cs:code, ds:bufseg, es:data
;the following externs are in 'memory'
extrn init_vars$: near
extrn insert_string$: near
;the following externs are in 'marks'
extrn init_marks: near
;the following externs are in 'redisp'
extrn adjust_buffers: near
select_buffer:
;enter with ds=buffer to select.
mov textseg,ds ;save the new current buffer.
mov ax,botbot
sub ax,toptop
mov dx,0
mov cx,100
div cx
mov memsize,ax
ret
init_buffer:
;initialize and opens the buffer whose segment is in dx.
;exit with cy if there's not enough memory for a new buffer.
mov ax,(offset bufseg_size)+0fh ;get size of bufseg rounded up.
shr ax,1
shr ax,1
shr ax,1
shr ax,1
add ax,dx
cmp ax,last_para ;is there enough memory for new buffer?
jae init_buffer_1 ;no.
push ds
mov ds,dx
mov bx,last_para
mov next_buffer,bx
push dx
call init_vars$ ;init most everything
call init_marks ;init the rest.
call open_buffer ;open this one up.
pop dx
pop ds
clc
ret
init_buffer_1:
stc
ret
close_buffer:
;close the buffer in ds.
mov ax,topbot ;if topbot<>bottop, then it's open.
cmp ax,bottop
je close_buffer_1 ;if it's already closed, we're done.
mov di,topbot
if 1
mov ax,ds ;if it's open and it's not the first
cmp ax,first_buffer ; buffer, close it.
jne close_buffer_2
add di,2000 ;leave at most this many free bytes
cmp di,bottop ; in buffer one.
jae close_buffer_1 ;but if we can't leave that many, don't bother
close_buffer_2:
endif
mov si,bottop
mov bottop,di ;save the new bottop.
mov cx,botbot
sub cx,si ;same as sub cx,bottop
add cx,2 ;include the trailing newline.
push es
push ds
pop es
rep movsb
pop es
sub di,2 ;don't include the newline in botbot.
mov botbot,di
close_buffer_1:
ret
open_buffer:
;select the buffer in ds.
;enter with ds=buffer to open.
mov bx,next_buffer
mov ax,ds
sub bx,ax ;compute the available paragraphs
cmp bx,1000h ;don't use more than 1000h of them.
jb open_buffer_1
mov bx,1000h
open_buffer_1:
mov di,bx
add bx,ax ;make bx=first unused para.
mov cl,04 ;change di from paras into bytes
shl di,cl
push es
push ds
pop es
mov si,botbot
std
mov cx,botbot
sub cx,bottop
inc si
dec di
movsb ;move the LF
mov botbot,di
movsb ;move the CR
rep movsb
inc di
mov bottop,di
cld
pop es
call select_buffer
ret
move_buffer_higher:
;move a buffer higher in memory, closing open buffers.
;enter with ds,bx=buffer to move, ax=first unavailable paragraph
;exit with ax=paragraph that buffer was moved to (new first unavailable para).
;don't destroy dx.
push ax
call close_buffer
pop ax
push es
call buffer_paragraphs
sub ax,cx ;find new destination para
call adjust_buffers
mov es,ax
assume es:bufseg
mov si,0
mov di,si
mov cx,botbot ;get the last location used in a buffer,
add cx,2-1 ; add two for newline and compensate for moving backwards.
add si,cx
add di,cx
; inc cx ;don't do this and the following dec
; ; because it wastes time.
std
movsb ;always move one byte.
; dec cx ;this takes care of moving 65536 bytes.
rep movsb
cld
pop es
assume es:data
ret
move_buffer_lower:
;move a buffer lower in memory.
;enter with ds=buffer before the one to be lowered.
;exit with ds=new location of lowered buffer.
call buffer_paragraphs
mov ax,ds ;get the base of this buffer.
add ax,cx ;get the end of this buffer.
mov bx,next_buffer
mov next_buffer,ax ;save the pointer to the next buffer.
mov ds,bx ;get paragraph of buffer to move.
cmp ax,bx ;leave if the buffer is already there.
je move_buffer_lower_1
call adjust_buffers
push es
mov es,ax
mov si,0
mov di,si
mov cx,botbot ;get the last location used in a buffer.
add cx,2 ;round up to next paragraph, add two for newline.
movsb ;always move one byte.
dec cx ;this takes care of moving 65536 bytes.
rep movsb
pop es
mov ds,ax ;get new para of just moved buffer.
move_buffer_lower_1:
ret
buffer_paragraphs:
;compute the number of paragraphs used by a buffer.
;enter with ds=buffer
;exit with cx=number of paragraphs.
mov cx,botbot
add cx,0fh+2 ;round up to next paragraph plus two for newline
rcr cx,1 ;ensure that 65536 bytes becomes
shr cx,1 ; 1000h paragraphs.
shr cx,1
shr cx,1
ret
code ends
end